package cn.gov.mwr.sl651.parser;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.imageio.ImageIO;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.gov.mwr.sl330.HydroParser;
import cn.gov.mwr.sl330.TypeCode;
import cn.gov.mwr.sl651.AbstractParser;
import cn.gov.mwr.sl651.IMessage;
import cn.gov.mwr.sl651.IMessageBody;
import cn.gov.mwr.sl651.IMessageHeader;
import cn.gov.mwr.sl651.IParser;
import cn.gov.mwr.sl651.Symbol;
import cn.gov.mwr.sl651.body.DataItem;
import cn.gov.mwr.sl651.body.Up30Body;
import cn.gov.mwr.sl651.body.Up31Body;
import cn.gov.mwr.sl651.body.Up35Body;
import cn.gov.mwr.sl651.body.Up36Body;
import cn.gov.mwr.sl651.body.Up40Body;
import cn.gov.mwr.sl651.body.Up41Body;
import cn.gov.mwr.sl651.body.Up44Body;
import cn.gov.mwr.sl651.body.UpBaseBody;
import cn.gov.mwr.sl651.body.UpE2Body;
import cn.gov.mwr.sl651.utils.ByteUtil;
import cn.gov.mwr.sl651.utils.CollectionUtil;
import cn.gov.mwr.sl651.utils.StcdParser;

/**
 * 字节解析类
 *
 * @author lipujun
 * @ClassName: HexParser
 * @Description: TODO
 * @date Mar 11, 2013
 */
public class HexParser extends AbstractParser implements IParser {

    private static Logger logger = LoggerFactory.getLogger(HexParser.class);
    /**
     * 分隔符
     */
    public final static String SEP = " ";

    /**
     * 附录C，标识符
     */
    private final Set<Byte> FC_MAP = CollectionUtil.treeSet((byte) 0xF0,
            (byte) 0xF1, (byte) 0xF2, (byte) 0xF3, (byte) 0xF4, (byte) 0xF5,
            (byte) 0xF6, (byte) 0xF7, (byte) 0xF8, (byte) 0xF9, (byte) 0xFA,
            (byte) 0xFB, (byte) 0xFC, (byte) 0xFD, (byte) 0x01, (byte) 0x02,
            (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
            (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B, (byte) 0x0C,
            (byte) 0x0D, (byte) 0x0E, (byte) 0x0F, (byte) 0x10, (byte) 0x11,
            (byte) 0x12, (byte) 0x13, (byte) 0x14, (byte) 0x15, (byte) 0x16,
            (byte) 0x17, (byte) 0x18, (byte) 0x19, (byte) 0x1A, (byte) 0x1B,
            (byte) 0x1C, (byte) 0x1D, (byte) 0x1E, (byte) 0x1F, (byte) 0x20,
            (byte) 0x21, (byte) 0x22, (byte) 0x23, (byte) 0x24, (byte) 0x25,
            (byte) 0x26, (byte) 0x27, (byte) 0x28, (byte) 0x29, (byte) 0x2A,
            (byte) 0x2B, (byte) 0x2C, (byte) 0x2D, (byte) 0x2E, (byte) 0x2F,
            (byte) 0x30, (byte) 0x31, (byte) 0x32, (byte) 0x33, (byte) 0x34,
            (byte) 0x35, (byte) 0x36, (byte) 0x37, (byte) 0x38, (byte) 0x39,
            (byte) 0x3A, (byte) 0x3B, (byte) 0x3C, (byte) 0x3D, (byte) 0x3E,
            (byte) 0x3F, (byte) 0x40, (byte) 0x41, (byte) 0x42, (byte) 0x43,
            (byte) 0x44, (byte) 0x45, (byte) 0x46, (byte) 0x47, (byte) 0x48,
            (byte) 0x49, (byte) 0x4A, (byte) 0x4B, (byte) 0x4C, (byte) 0x4D,
            (byte) 0x4E, (byte) 0x4F, (byte) 0x50, (byte) 0x51, (byte) 0x52,
            (byte) 0x53, (byte) 0x54, (byte) 0x55, (byte) 0x56, (byte) 0x57,
            (byte) 0x58, (byte) 0x59, (byte) 0x5A, (byte) 0x5B, (byte) 0x5C,
            (byte) 0x5D, (byte) 0x5E, (byte) 0x5F, (byte) 0x60, (byte) 0x61,
            (byte) 0x62, (byte) 0x63, (byte) 0x64, (byte) 0x65, (byte) 0x66,
            (byte) 0x67, (byte) 0x68, (byte) 0x69, (byte) 0x6A, (byte) 0x6B,
            (byte) 0x6C, (byte) 0x6D, (byte) 0x6E, (byte) 0x6F, (byte) 0x70,
            (byte) 0x71, (byte) 0x72, (byte) 0x73, (byte) 0x74, (byte) 0x75,
            (byte) 0xFF, (byte) 0xFD);

    /**
     * 附录C，固定字节标识符
     */

    private final static ConcurrentHashMap FC_DATA_MAP = new ConcurrentHashMap();

    static {
        FC_DATA_MAP.put((byte) 0xF0, 5);
        FC_DATA_MAP.put((byte) 0xF1, 5);
        FC_DATA_MAP.put((byte) 0xF4, 12);
        FC_DATA_MAP.put((byte) 0xF5, 24);
        FC_DATA_MAP.put((byte) 0xF6, 24);
        FC_DATA_MAP.put((byte) 0xF7, 24);
        FC_DATA_MAP.put((byte) 0xF8, 24);
        FC_DATA_MAP.put((byte) 0xF9, 24);
        FC_DATA_MAP.put((byte) 0xFA, 24);
        FC_DATA_MAP.put((byte) 0xFB, 24);
        FC_DATA_MAP.put((byte) 0xFC, 24);
        FC_DATA_MAP.put((byte) 0x45, 4);
        FC_DATA_MAP.put((byte) 0xFF, 2);
        FC_DATA_MAP.put((byte) 0xFD, 24);
    }

    /**
     * 解析发送时间
     *
     * @param data
     * @return
     * @throws Exception
     */
    public Date parseSendTime(byte[] data) throws Exception {

        String datestr = ByteUtil.bcd2Str(data);
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmss");
        return sdf.parse(datestr);
    }

    /**
     * 解析遥测站地址编码，参见6.2.3.2
     *
     * @param stcd
     */
    public String parseStcd(byte[] data) {
        return StcdParser.parseStcd(data);
    }

    /**
     * 将16进制解析10进制数据，中心站地址范围为1～255。
     *
     * @param hexString
     * @return
     */
    public int parseCenterAddr(byte[] bytes) {
        return ByteUtil.bytesToUbyte(bytes);
    }

    /**
     * 解析报文"功能码"
     */
    public byte parseFuncCode(byte[] bytes) {
        return bytes[0];
    }

    /**
     * 解析报文"序列号"
     */
    public int parseSerial(byte[] bytes) {

        return ByteUtil.bytesToUshort(bytes);
    }

    /**
     * 解析报文"报文数据超始位"
     */
    public byte parseBodyStartBit(byte[] bytes) {
        return bytes[0];
    }

    public IMessageBody parseBody(byte funcCode, byte[] bodyContents)
            throws Exception {

        logger.info(">> 开始解析 【" + ByteUtil.toHexString(funcCode) + "】 号报文.....");

        IMessageBody body = null;
        switch (funcCode) {
            case 0x2F:
            case 0x30: // 测试报
            case 0x32: // 定时报
            case 0x33: // 加报报
            case 0x34: // 小时报，以1小时为基本单位向中心站报送遥测站水文信息
            case 0x37: // 6.6.4.10　中心站查询遥测站实时数据
                body = parse30Body(bodyContents);
                break;
            case 0x36: // 图片报
                body = parse36Body(bodyContents);
                break;
            case 0x31: // 均匀时段报
            case 0x38: // 查询时段数据的上行报文
                body = parse31Body(bodyContents);
                break;
            case 0x35: // 人工置数报
            case 0x39: // 查询人工数据的上行报文
                body = parse35Body(bodyContents);
                break;
            // case 0x36: // 图片报
            // body = parse36Body(messageBody);
            // break;
            case 0x40: // 6.6.4.14　中心站修改遥测站基本配置表
            case 0x41: // 6.6.4.15　中心站读取遥测站基本配置表/遥测站自报基本配置表
                body = parse40Body(bodyContents);
                break;
            case 0x42: // 6.6.4.16　中心站修改遥测站运行参数配置表
            case 0x43: // 6.6.4.17　中心站读取遥测站运行参数配置表/遥测站自报运行参数配置表
                body = parse42Body(bodyContents);
                break;
            case 0x44: // 6.6.4.26　控制水泵开关命令/水泵状态自报
                body = parse44Body(bodyContents);
                break;
            case 0x45: // 6.6.4.26　控制水泵开关命令/水泵状态自报
                body = parse45Body(bodyContents);
                break;
            case 0x46: // 6.6.4.26　控制水泵开关命令/水泵状态自报
            case 0x4B: // 6.6.4.26　控制水泵开关命令/水泵状态自报
                body = parse46Body(bodyContents);
                break;
            case 0x47: // 6.6.4.21　初始化固态存储数据
            case 0x48: // 6.6.4.22　恢复遥测站出厂设置
            case 0x4A: // 6.6.4.24　设置遥测站时钟
            case 0x51: // 6.6.4.31　中心站查询遥测站时钟
                body = parseUpBaseBody(bodyContents);
                break;
            // case 0x48: // 6.6.4.22　恢复遥测站出厂设置
            // body = parse48Body(messageBody);
            // break;
            case 0x49: // 6.6.4.23　修改密码
                body = parse49Body(bodyContents);
                break;
            // case 0x4A: // 6.6.4.24　设置遥测站时钟
            // body = parse4ABody(messageBody);
            // break;

            case 0x4C: // 6.6.4.26　控制水泵开关命令/水泵状态自报
            case 0x4D: // 6.6.4.27　控制阀门开关命令/阀门状态信息自报
                body = parse4CBody(bodyContents);
                break;
            case 0x4E: // 6.6.4.28　控制闸门开关命令/闸门状态信息自报
                body = parse4EBody(bodyContents);
                break;
            case 0x4F: // 6.6.4.29　水量定值控制命令
                body = parse4FBody(bodyContents);
                break;
            case 0x50: // 6.6.4.30　中心站查询遥测站事件记录
                body = parse50Body(bodyContents);
                break;
            // case 0x51: // 6.6.4.31　中心站查询遥测站时钟
            // body = parse51Body(messageBody);
            // break;
            case (byte) 0xE2: // 6.6.4.23　修改密码
                body = parseE2Body(bodyContents);
                break;
            default:
                body = parse30Body(bodyContents);
                break;
        }

        return body;
    }

    /**
     * 解析 case 0x30: // 测试报 case 0x32: // 定时报 case 0x33: // 加报报 case 0x34: //
     * 小时报，以1小时为基本单位向中心站报送遥测站水文信息 case 0x36: // 图片报 case 0x37: //
     * 6.6.4.10　中心站查询遥测站实时数据
     *
     * @param messageBody
     * @return
     * @throws Exception
     */
    public Up30Body parse30Body(byte[] bytes) throws Exception {
        Up30Body body = new Up30Body();

        int pos = 0;

        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        if (bytes.length > 8) {

            // 遥测站地址
            byte[] stcdFlag = new byte[2];
            byte[] stcd = new byte[5];
            System.arraycopy(bytes, pos, stcdFlag, 0, stcdFlag.length);
            pos = pos + stcdFlag.length;
            System.arraycopy(bytes, pos, stcd, 0, stcd.length);
            pos = pos + stcd.length;
            String stationAddr = parseStcd(stcd);
            body.setStcd(stationAddr);

            // 遥测站分类码
            byte[] category = new byte[1];
            System.arraycopy(bytes, pos, category, 0, category.length);
            body.setSttp(new String(category));
            pos = pos + category.length;

            // 观测时间
            byte[] viewDateFlag = new byte[2];
            byte[] viewDateByte = new byte[5];
            System.arraycopy(bytes, pos, viewDateFlag, 0, viewDateFlag.length);
            pos = pos + viewDateFlag.length;
            System.arraycopy(bytes, pos, viewDateByte, 0, viewDateByte.length);
            pos = pos + viewDateByte.length;
            body.setViewDate(ByteUtil.bcd2Str(viewDateByte));
            // 数据内容
            int len = bytes.length - pos;
            byte[] data = new byte[len];
            System.arraycopy(bytes, pos, data, 0, len);
            List<DataItem> itemList = new ArrayList<DataItem>();
            // 开始读取业务数据
            for (int i = 0, ln = data.length; i < ln; ) {
                String extend = "";
                byte flag = data[i];
                i = i + 1;
                byte dataLenFlag = data[i];
                i = i + 1;
                int tempDataLen;
                int tempdecimal = 0;
                if ((byte) 0xF3 == flag || (byte) 0xF2 == flag) {
                    tempDataLen = ln - 2;// 一般此类数据中，只包含此数据，所以数据长度 = 总长度 -
                    // 数据标识符标识长度
                } else if (flag == (byte) 0XFD) {
                    byte[] fdcn = new byte[1];
                    System.arraycopy(data, i - 1, fdcn, 0, fdcn.length);
                    if (fdcn[0] == (byte) 0xc1 || fdcn[0] == (byte) 0xc2 || fdcn[0] == (byte) 0xc3) {
                        tempDataLen = 24;
                    } else {
                        tempDataLen = 6;
                    }

                } else if (flag == (byte) 0XFF) {
                    byte[] fdcn = new byte[1];
                    System.arraycopy(data, i - 1, fdcn, 0, fdcn.length);
                    if (fdcn[0] == (byte) 0x02) {
                        tempDataLen = 4;
                        extend = "02";
                        tempdecimal = 2;
                    } else if (fdcn[0] == (byte) 0x03) {
                        tempDataLen = 2;
                        extend = "03";
                        tempdecimal = 0;
                    } else if (fdcn[0] == (byte) 0x04) {
                        tempDataLen = 6;
                        extend = "04";
                        tempdecimal = 3;
                    } else if (fdcn[0] == (byte) 0x05) {
                        tempDataLen = 6;
                        extend = "05";
                        tempdecimal = 3;
                    } else if (fdcn[0] == (byte) 0xC9) {
                        tempDataLen = 4;
                        extend = "C9";
                        tempdecimal = 1;
                    } else if (fdcn[0] == (byte) 0x08) {
                        tempDataLen = 3;
                        extend = "08";
                        tempdecimal = 0;
                    } else {
                        tempDataLen = 3;
                        extend = "00";
                        tempdecimal = 0;

                    }

                } else if (FC_DATA_MAP.containsKey(flag)) {
                    tempDataLen = (Integer) FC_DATA_MAP.get(flag);
                } else {
                    int[] dataLen = parseDataLength(dataLenFlag);
                    tempDataLen = dataLen[0];
                    tempdecimal = dataLen[1];
                }
                byte[] temp = new byte[tempDataLen];
                System.arraycopy(data, i, temp, 0, tempDataLen);
                System.out.println("-----标识符-- " + ByteUtil.toHexString(flag) + "---扩展协调字段----" + extend + " -----开始读取" + tempDataLen + "个字节---------tempdecimal " + tempdecimal + " value " + ByteUtil.toHexString(temp));
                DataItem item = new DataItem();
                item.setLength(tempDataLen);
                item.setDecimal(tempdecimal);
                item.setLabel(new byte[]{flag});
                item.setValue(temp);
                item.setExtend(extend);
                itemList.add(item);
                i = i + tempDataLen;
            }
            body.setItems(itemList);
        }

        return body;

    }

    /**
     * 解析时段数据报
     *
     * @param messageBody
     * @return
     * @throws Exception
     */
    public Up31Body parse31Body(byte[] bytes) throws Exception {
        Up31Body body = new Up31Body();

        int pos = 0;

        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        // 遥测站地址
        byte[] stcdFlag = new byte[2];
        byte[] stcd = new byte[5];
        System.arraycopy(bytes, pos, stcdFlag, 0, stcdFlag.length);
        pos = pos + stcdFlag.length;
        System.arraycopy(bytes, pos, stcd, 0, stcd.length);
        pos = pos + stcd.length;
        String stationAddr = parseStcd(stcd);
        body.setStcd(stationAddr);

        // 遥测站分类码
        byte[] category = new byte[1];
        System.arraycopy(bytes, pos, category, 0, category.length);
        body.setSttp(new String(category));
        pos = pos + category.length;

        // 观测时间
        byte[] viewDateFlag = new byte[2];
        byte[] viewDateByte = new byte[5];
        System.arraycopy(bytes, pos, viewDateFlag, 0, viewDateFlag.length);
        pos = pos + viewDateFlag.length;
        System.arraycopy(bytes, pos, viewDateByte, 0, viewDateByte.length);
        pos = pos + viewDateByte.length;
        body.setViewDate(ByteUtil.bcd2Str(viewDateByte));

        // 时间步长码
        byte[] drxxxByte = new byte[3];
        pos = pos + 2;
        System.arraycopy(bytes, pos, drxxxByte, 0, drxxxByte.length);
        pos = pos + drxxxByte.length;
        body.setDrxxx(drxxxByte);

        // 要素标识符，因为是HEX解码，所以只取两个字节
        byte[] itemFlag = new byte[2];
        System.arraycopy(bytes, pos, itemFlag, 0, itemFlag.length);
        pos = pos + itemFlag.length;
        body.setItemFlag(itemFlag);

        // 读取数据
        int len = bytes.length - pos;
        byte[] data = new byte[len];
        System.arraycopy(bytes, pos, data, 0, len);

        int[] dataLen = parseDataLength(itemFlag[1]);
        int tempDataLen = dataLen[0];
        int decimal = dataLen[1];
        // 此处添加特殊字符判断，雨量字节长度为一个字节，水位字节长度为2个字节
        if (Arrays.equals(itemFlag, new byte[]{(byte) 0xF4, (byte) 0xf4})
                || Arrays.equals(itemFlag, new byte[]{(byte) 0xF4,
                (byte) 0x60})) {
            // if (body.getSttp().equalsIgnoreCase("P")) {
            tempDataLen = 1;
            decimal = 1;
            // }
        }

        if (Arrays.equals(itemFlag, new byte[]{(byte) 0xF5, (byte) 0xF5})
                || Arrays.equals(itemFlag, new byte[]{(byte) 0xF5,
                (byte) 0xC0})) {
            tempDataLen = 2;
            decimal = 2;
        }

        List<DataItem> itemList = new ArrayList<DataItem>();
        // -----------2014-08-09修改---------------------
        DataItem item = new DataItem();
        item.setLength(tempDataLen);
        item.setDecimal(decimal);
        item.setLabel(new byte[]{itemFlag[0]});// ByteUtil.toHexString(itemFlag[0]).getBytes()
        item.setValue(data);
        itemList.add(item);
        body.setItems(itemList);

        // -----------以前的代码，也没有问题，调整策略--------------------

        // List<DataItem> itemList = new ArrayList<DataItem>();
        //
        // if (tempDataLen != 0) {
        // for (int i = 0; i < len;) {
        // if (len < tempDataLen) {
        // tempDataLen = len;
        // }
        // byte[] temp = new byte[tempDataLen];
        // System.arraycopy(data, i, temp, 0, tempDataLen);
        //
        // logger.info(">> 标识符：" + ByteUtil.toHexString(itemFlag[0])
        // + "\t字节长度：" + tempDataLen + "\t精度：" + decimal + "\t值："
        // + ByteUtil.bcd2Str(temp));
        //
        // DataItem item = new DataItem();
        // item.setLength(tempDataLen);
        // item.setDecimal(decimal);
        // item.setLabel(new byte[] { itemFlag[0] });//
        // ByteUtil.toHexString(itemFlag[0]).getBytes()
        // item.setValue(temp);
        // itemList.add(item);
        //
        // i = i + tempDataLen;
        //
        // }
        // }
        // body.setItems(itemList);

        return body;
    }

    /**
     * 解析 “人工置数报”，功能码：35H
     * <p>
     * 假定报文中的信息为完整的“人工置数报”
     */
    public void parse35Body(IMessage msg) throws Exception {
        IMessageBody body = msg.getBody();
        IMessageHeader header = msg.getHeader();

        byte[] content = body.getContent();

        String messageBody = new String(content);// 将byte[]转换为String

        // 第一步：先拆分数据，将其分组
        String[] data = StringUtils.split(messageBody, SEP);

        // 第二步：根据标识符关键字，查找标识符数据
        // String cate = data[0];
        // cate.getBytes();
        // ByteUtil.bytesToUbyte(array)
        // switch()
        String cate = data[0];

        int typeCode = ByteUtil.bytesToUbyte(cate.getBytes());
        String dataStr = "";

        switch (typeCode) {
            case TypeCode.P:// 降水
                HydroParser.parseRgzsP(messageBody);
                break;
            case TypeCode.H:// 河道
                HydroParser.parseRgzsH(messageBody);
                break;
            case TypeCode.K:// 水库（湖泊）
                HydroParser.parseRgzsK(messageBody);
                break;
            case TypeCode.Z:// 闸坝
                HydroParser.parseRgzsZ(messageBody);
                break;
            case TypeCode.D:// 泵站
                HydroParser.parseRgzsD(messageBody);
                break;
            case TypeCode.T:// 潮汐
                HydroParser.parseRgzsT(messageBody);
                break;
            case TypeCode.M:// 土壤墒情
                HydroParser.parseRgzsM(messageBody);
                break;
            case TypeCode.G:// 地下水
                HydroParser.parseRgzsG(messageBody);
                break;
            case TypeCode.Q:// 水质
                // parseRgzsQ();
                break;
            case TypeCode.I:// 取水口
                // parseRgzsI();
                break;
            case TypeCode.O:// 排水口
                // parseRgzsO();
                break;
            default:
                break;
        }

        for (int i = 0, len = data.length; i < len; i++) {

        }

        // 第三步：写入数据库

    }

    /**
     * 解析人工置数报
     *
     * @param messageBody
     * @return
     */
    public Up35Body parse35Body(byte[] bytes) throws Exception {
        Up35Body body = new Up35Body();

        int pos = 0;

        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        // 人工置数标识符
        byte flag = (byte) 0xF2;
        body.setFlag(flag);
        pos = pos + 1;

        // 人工置数数据
        int len = bytes.length - pos;
        byte[] data = new byte[len];
        System.arraycopy(bytes, pos, data, 0, len);
        body.setRGZS(new String(data));

        return body;
    }

    /**
     * 解析 “图片报”，功能码：36H
     * <p>
     * 假定报文中的信息为完整的“图片信息”
     */

    public IMessageBody parse36Body(byte[] bytes) throws Exception {

        System.out.println("----------------parse36Body------------");

        Up36Body body = new Up36Body();

        int pos = 0;

        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        if (bytes.length > 8) {

            // 遥测站地址
            byte[] stcdFlag = new byte[2];
            byte[] stcd = new byte[5];
            System.arraycopy(bytes, pos, stcdFlag, 0, stcdFlag.length);
            pos = pos + stcdFlag.length;
            System.arraycopy(bytes, pos, stcd, 0, stcd.length);
            pos = pos + stcd.length;
            String stationAddr = parseStcd(stcd);
            body.setStcd(stationAddr);

            // 遥测站分类码
            byte[] category = new byte[1];
            System.arraycopy(bytes, pos, category, 0, category.length);
            body.setSttp(new String(category));
            pos = pos + category.length;

            // 观测时间
            byte[] viewDateFlag = new byte[2];
            byte[] viewDateByte = new byte[5];
            System.arraycopy(bytes, pos, viewDateFlag, 0, viewDateFlag.length);
            pos = pos + viewDateFlag.length;
            System.arraycopy(bytes, pos, viewDateByte, 0, viewDateByte.length);
            pos = pos + viewDateByte.length;
            body.setViewDate(ByteUtil.bcd2Str(viewDateByte));

            // 图片标识
            byte[] imgFlag = new byte[2];
            System.arraycopy(bytes, pos, imgFlag, 0, imgFlag.length);
            pos = pos + imgFlag.length;
            body.setFlag(imgFlag);

            // 图片数据
            int len = bytes.length - pos;
            byte[] data = new byte[len];
            System.arraycopy(bytes, pos, data, 0, len);
            body.setImg(data);

        }

        return body;

    }

    public void parse36Body() {
        System.out.println("----------------parse36Body------------");
        Up30Body body36 = new Up30Body();

        String stcd = body36.getStcd();
        String viewDate = body36.getViewDate();
        String fileName = "c:\\" + stcd + "_" + viewDate + "." + "jpg";
        // writeImage(body.getContent(), fileName);
    }

    private void writeImage(byte[] bytes, String fileName) {

        System.out.println("----------------writeImage------------");

        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);

        try {
            BufferedImage bufferedImage = ImageIO.read(bais);

            // ImageIO.write(bufferedImage, "PNG", new File(
            // "d:\\yourImageName.PNG"));// 输出到 png 文件
            ImageIO.write(bufferedImage, "JPEG", new File(fileName));// 输出到
            // jpg文件
            // ImageIO.write(bufferedImage, "gif", new
            // File("d:\\yourImageName.GIF"));//输出到 gif 文件
            // ImageIO.write(bufferedImage, "BMP", new
            // File("d:\\yourImageName.BMP"));//输出到 bmp 文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 6.6.4.19　中心站查询遥测站软件版本
     *
     * @param messageBody
     * @return
     * @throws Exception
     */
    public Up40Body parse40Body(byte[] bytes) throws Exception {
        Up40Body body = new Up40Body();

        int pos = 0;
        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        byte[] stationSymbol = new byte[2];
        System.arraycopy(bytes, pos, stationSymbol, 0, stationSymbol.length);
        pos = pos + stationSymbol.length;

        byte[] stationAddr = new byte[5];
        System.arraycopy(bytes, pos, stationAddr, 0, stationAddr.length);

        body.setStcd(parseStcd(stationAddr));
        pos = pos + stationAddr.length;

        // 数据内容
        int len = bytes.length - pos;
        byte[] data = new byte[len];
        System.arraycopy(bytes, pos, data, 0, len);

        List<DataItem> itemList = new ArrayList<DataItem>();

        // 开始读取业务数据
        for (int i = 0, ln = data.length; i < ln; ) {
            byte flag = data[i];
            i = i + 1;
            byte dataLenFlag = data[i];
            i = i + 1;
            int tempDataLen = (int) (dataLenFlag >> 3 & 0x1F);// 获取前五位的数据
            int tempdecimal = (int) (dataLenFlag & 0x07); // 获取后三位的小数点位数

            byte[] temp = new byte[tempDataLen];
            System.arraycopy(data, i, temp, 0, tempDataLen);

            // System.out.println(">>运行参数 -----标识符-- "
            // + ByteUtil.toHexString(flag) + " -----开始读取" + tempDataLen
            // + "个字节---------tempdecimal " + tempdecimal + " value "
            // + ByteUtil.bcd2Str(temp));

            DataItem item = new DataItem();
            item.setLength(tempDataLen);
            item.setDecimal(tempdecimal);
            item.setLabel(new byte[]{flag, dataLenFlag});
            item.setValue(temp);
            itemList.add(item);

            i = i + tempDataLen;
        }

        body.setItems(itemList);

        return body;
    }

    /**
     * 6.6.4.15　中心站读取遥测站基本配置表/遥测站自报基本配置表
     *
     * @param messageBody
     * @return
     * @throws Exception
     */
    public Up41Body parse41Body(byte[] bytes) throws Exception {
        return (Up41Body) parse40Body(bytes);
    }

    public Up44Body parse44Body(byte[] bytes) throws Exception {
        Up44Body body = new Up44Body();

        int pos = 0;

        // 序列号
        byte[] serialByte = new byte[2];
        System.arraycopy(bytes, pos, serialByte, 0, serialByte.length);
        pos = pos + serialByte.length;
        body.setSerialId(ByteUtil.bytesToUshort(serialByte));

        // 发报时间
        byte[] sendDateByte = new byte[6];
        System.arraycopy(bytes, pos, sendDateByte, 0, sendDateByte.length);
        body.setSendDate(ByteUtil.bcd2Str(sendDateByte));
        pos = pos + sendDateByte.length;

        if (bytes.length > 8) {

            // 遥测站地址
            byte[] stcdFlag = new byte[2];
            byte[] stcd = new byte[5];
            System.arraycopy(bytes, pos, stcdFlag, 0, stcdFlag.length);
            pos = pos + stcdFlag.length;
            System.arraycopy(bytes, pos, stcd, 0, stcd.length);
            pos = pos + stcd.length;
            String stationAddr = parseStcd(stcd);
            body.setStcd(stationAddr);

            // 遥测站分类码
            byte[] category = new byte[1];
            System.arraycopy(bytes, pos, category, 0, category.length);
            body.setSttp(new String(category));
            pos = pos + category.length;

            // 观测时间
            byte[] viewDateFlag = new byte[2];
            byte[] viewDateByte = new byte[5];
            System.arraycopy(bytes, pos, viewDateFlag, 0, viewDateFlag.length);
            pos = pos + viewDateFlag.length;
            System.arraycopy(bytes, pos, viewDateByte, 0, viewDateByte.length);
            pos = pos + viewDateByte.length;
            body.setViewDate(ByteUtil.bcd2Str(viewDateByte));

            System.out.println("----------------------------------------");
            System.out.println(">> serial := " + body.getSerialId()
                    + "\tsendDate := " + body.getSendDate()
                    + "\tstationAddr := " + stationAddr);
            System.out.println("----------------------------------------");

            // 数据内容
            int len = bytes.length - pos;
            byte[] data = new byte[len];
            System.arraycopy(bytes, pos, data, 0, len);

            List<DataItem> itemList = new ArrayList<DataItem>();

            // 开始读取业务数据
            for (int i = 0, ln = data.length; i < ln; ) {
                byte flag = data[i];
                i = i + 1;
                byte dataLenFlag = data[i];
                i = i + 1;
                int tempDataLen;
                int tempdecimal = 0;
                if ((byte) 0xF3 == flag || (byte) 0xF2 == flag) {
                    tempDataLen = ln - 2;// 一般此类数据中，只包含此数据，所以数据长度 = 总长度 -
                    // 数据标识符标识长度
                } else if (FC_DATA_MAP.containsKey(flag)) {
                    tempDataLen = (Integer) FC_DATA_MAP.get(flag);
                } else {
                    int[] dataLen = parseDataLength(dataLenFlag);
                    tempDataLen = dataLen[0];
                    tempdecimal = dataLen[1];
                }

                byte[] temp = new byte[tempDataLen];
                System.arraycopy(data, i, temp, 0, tempDataLen);

                System.out.println("-----标识符-- " + ByteUtil.toHexString(flag)
                        + " -----开始读取" + tempDataLen
                        + "个字节---------tempdecimal " + tempdecimal + " value "
                        + ByteUtil.bcd2Str(temp));

                DataItem item = new DataItem();
                item.setLength(tempDataLen);
                item.setDecimal(tempdecimal);
                item.setLabel(new byte[]{flag});
                item.setValue(temp);
                itemList.add(item);

                i = i + tempDataLen;
            }
            body.setItems(itemList);
        }

        return body;

    }

    /**
     * 消息体解析方法
     * <p>
     * // 第一步：按字节获取值
     * <p>
     * // 第二步：将其转化为HEX编码
     * <p>
     * // 第三步：将其与功能码比较
     * <p>
     * // 第四步：处理逻辑
     *
     * @param body
     */
    public void parseBody2(IMessageBody body) {
        byte[] content = body.getContent();
        // 开始读字节
        for (int i = 0, len = content.length; i < len; i++) {
            int move = 0;
            byte flag = content[i];

            // 看读取的字节是否在标识符内，如果在，则需要向后读取一个数据定义位字节
            if (FC_MAP.contains(flag)) {
                // 向后读取一个字节
                move = move + 1;
            }

            if (flag == 0xFF) {
                // 此处针对自定义的特殊情况处理
                move = move + 2;
            }

            // 读取数据，添加数据读取不为0的情况，一般情况下不会为0，如果为0，则数据有可能已经出现错误
            if (move != 0) {
                byte dataLenFlag = content[i + move];
                int[] dataLen = parseDataLength(dataLenFlag);
                byte[] dest = new byte[dataLen[0]];
                System.arraycopy(content, i + move, dest, 0, dest.length);
                move = move + dataLen[0];

                // 解析数据值

                // 读完数据的业务处理

            }

            i = i + move;
        }

    }

    public UpE2Body parseE2Body(byte[] bytes) throws Exception {
        UpE2Body body = new UpE2Body();
        body.setContents(bytes);
        return body;
    }

    /**
     * 解析报文的模式，相当于只在此解析为M1,M2,M3模式
     * <p>
     * 注意点：此方法可改到外部
     *
     * @param IMessageHeader
     */
    public String parseMode(IMessageHeader header) {
        String mode = "M2";

        // 根据功能码判定为M1模式，功能码在外层做判断，不进入本层判断
        byte[] funcCode = header.getFuncCode();
        if (funcCode[0] == 0x2F) {
            mode = "M1";
        }

        // 根据报文起始符判定为使用M2或M3模式
        byte[] bodyStartBit = header.getBodyStartBit();
        if (bodyStartBit[0] == Symbol.SYN) {
            mode = "M3";
        } else {
            mode = "M2";
        }

        return mode;
    }

    /**
     * 构造“包总数及序列号”
     *
     * @param count
     * @param serial
     * @return
     */
    public byte[] buildBodyCount(int count, int serial) {
        byte[] countByte = ByteUtil.ushortToBytes(count);
        byte high = (byte) ((countByte[1] << 4) & 0xF0);

        byte first = (byte) (((countByte[0] & 0x0F) << 4) | ((countByte[1] & 0xF0) >> 4));

        byte[] serialByte = ByteUtil.ushortToBytes(serial);
        byte low = (byte) (serialByte[0] & 0x0f);

        byte middle = (byte) (high | low);

        return new byte[]{first, middle, serialByte[1]};
    }

    /**
     * 解析报文"包总数及序列号"
     */
    public int[] parseBodyCount(byte[] bytes) {

        byte high = (byte) ((bytes[0] & 0xF0) >> 4);
        byte low = (byte) (((bytes[0] & 0x0F) << 4) | ((bytes[1] & 0xF0) >> 4));

        byte[] countBytes = new byte[]{high, low};

        byte[] serialBytes = new byte[]{(byte) (bytes[1] & 0x0F), bytes[2]};

        int count = ByteUtil.bytesToUshort(countBytes);
        int serial = ByteUtil.bytesToUshort(serialBytes);
        return new int[]{count, serial};
    }

    /**
     * 构造“报文上下行标识及长度”
     *
     * @param upDown
     * @param length
     * @return
     */
    public byte[] buildBodyLength(byte upDown, int length) {
        byte[] lenByte = ByteUtil.ushortToBytes(length);
        byte low4 = (byte) (lenByte[0] & 0x0f);
        byte first = (byte) (upDown | low4);
        return new byte[]{first, lenByte[1]};
    }

    /**
     * 解析报文上下行标识符及长度
     */
    public int[] parseBodyLength(byte[] bytes) {

        byte flag = (byte) ((bytes[0] >> 4) & 0x0f);

        byte high = (byte) (bytes[0] & 0x0f);
        byte low = bytes[1];

        int len = ByteUtil.bytesToUshort(new byte[]{high, low});
        return new int[]{flag, len};
    }

    /**
     * 获取数据的位数的长度和小数点长度，每个报文体中需用到多次次类
     * <p>
     * 注意：报文体中数据定义的几个固定值F0、F1、F2、F3、F6、
     * <p>
     * 优化：此处可以用缓存存储，以后再做修改
     *
     * @param data
     * @return
     * @desc 对应协议中6.6.3.3
     */
    public static int[] parseDataLength(byte data) {
        int[] result = new int[2];

        int datalen = (int) (data >> 3 & 0x1F);// 获取前五位的数据
        int decimal = (int) (data & 0x07); // 获取后三位的小数点位数

        switch ((byte) data) {
            case (byte) 0xF0: // 观测时间编码长度固定
                datalen = 1;
                decimal = 0;
                break;
            case (byte) 0xF1: // 遥测站地址编码长度固定
                datalen = 5;
                decimal = 0;
                break;
            case (byte) 0xF2: // 人工置数的长度一般超过32字节
            case (byte) 0xF3: // 图片数据的长度一般超过32字节
            case (byte) 0xF6: // 流量（流速）批量数据的长度一般超过32字节
                datalen = 32;
                decimal = 0;
                break;
            case (byte) 0xFF: // 流量（流速）批量数据的长度一般超过32字节
                datalen = 1;
                decimal = 0;
                break;
            default:

        }

        // 需要对固定数据定义的处理
        // 观测时间编码长度固定，因此HEX/BCD编码时数据定义字节固定用F0H表示。
        // 遥测站地址编码长度固定，因此HEX/BCD编码时数据定义字节固定用F1H表示。
        // 人工置数的长度一般超过32字节，且报文中只有人工置数数据，标识符之后全是其数据，数据字节长度易于辨识，因此HEX/BCD编码时数据定义字节固定用F2H表示。
        // 图片数据的长度一般超过32字节，且报文中只有图片数据，标识符之后全是其数据，数据字节长度易于辨识，因此HEX/BCD编码时数据定义字节固定用F3H表示。
        // 1字节HEX码，表示的降水量最大值为25.5毫米。当5分钟降水量超过此值时，将多出的量计入下一个5分钟内，此后处理以此类推；
        // 流量（流速）批量数据的长度一般超过32字节，且报文中只有这一类数据，数据字节长度易于辨识，因此HEX/BCD编码时数据定义字节固定用F6H表示。
        // ASCⅡ字符编码报文时间步长码数据结构取值范围应按表C.2的规定执行。时间步长码标识符为DRxnn，DR为时间步长码标志；x取D、H、N分别表示步长单位日、小时和分钟；nn表示对应取值范围。
        // HEX/BCD编码时间步长码标识符引导符是04H，数据定义用18H（3字节长度，无小数），用dhm表示步长码的数据构成，d、h、m各占1个字节BCD，分别表示步长单位日、小时和分钟；dhm中一般只能有一个字节不为00；时间步长码数据结构应按表C.3的规定执行。。
        // 当时间步长码DRHnn或0418H
        // dhm中的nn或dhm值是全“0”时，其后编报的标识符必须是DRP、DRZ类的1小时时段组合数据。这个特定的时间步长码与1小时时段组合数据标识符构成固定搭配。
        result[0] = datalen;
        result[1] = decimal;
        // System.out.println(">> len " + datalen + " ,decimal " + decimal);
        return result;
    }

    public static void main(String[] args) {

        byte[] data = new byte[]{(byte) 0x80, (byte) 0x01};
        String datastr = ByteUtil.toBinaryString(data);

        byte xxx = (byte) 0x2b;

        int tempDataLen = (int) (xxx >> 3 & 0x1F);// 获取前五位的数据
        int tempdecimal = (int) (xxx & 0x07); // 获取后三位的小数点位数

        System.out.println(" len " + tempDataLen + " dec " + tempdecimal);

        System.out.println(datastr);
        /**
         * 报文30的内容
         */
        StringBuffer sb = new StringBuffer();
        String aaa = "0123456789A";
        for (int i = 0; i < aaa.length(); i++) {
            System.out.println(">> " + i + " " + aaa.substring(i, i + 1));
        }

        // sb.append("7E7E020000000002000A32004702");
        // sb.append("001D130329160004");
        // sb.append("F1F1");
        // sb.append("0000000002");
        // sb.append("48");
        // sb.append("F0F013032916002019000000");
        // sb.append("F0F01303291505F460000000000000000000000000");
        // sb.append("F0F01303291600261900000039230003951038121219");
        // sb.append("03EC2D");

        /**
         * 报文31的内容
         */
        // StringBuffer sb = new StringBuffer();
        // 7E7E020000000002000A
        // 31
        // 004E
        // 02
        // sb.append("001C");
        // sb.append("130329160003");
        // sb.append("F1F10000000002");
        // sb.append("48");
        // sb.append("F0F01303291505");
        // sb.append("0418000005");
        // sb
        // .append("3923000395100003951000039510000395100003951000039510000395100003951000039510000395100003951000039510");
        // 03 B58C

        // 7E7E020000000002000A31004E02
        sb.append("0016130403170004F1F1000000000248F0F01304031605041800000539230003567000036000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00036000");
        // 0328BC

        /**
         * 报文34的内容
         */
        // 7e7e020000000002000a34005d02
        // sb.append("000e130403150009f1f1000000000248f0f01304031405f4600000ffffffffffffffffff00f0f013040315002619000000f0f01304031405f5c00def0defffffffffffffffffffffffffffffffffffff0deff0f0130403150038121219");
        // 034e0d

        /**
         * 报文32的内容
         */
        // 7E7E020000000002000A32004702
        // sb.append("0017130403170005F1F1000000000248F0F013040317002019000018F0F01304031605F4600406FFFFFFFFFFFFFFFFFF00F0F01304031700261900001839230003600038121219");
        // 03C3BC
        HexParser parser = new HexParser();
        byte[] xx = parser.buildBodyCount(199, 1);

        System.out.println(">> " + ByteUtil.byteToHexString(xx));

//		String hex = sb.toString();
//		byte[] content = ByteUtil.HexStringToBinary(hex);
//
//		UpBaseBody body = new UpBaseBody();
//		body.setContents(content); 
//
//		try {
//			// parser.parse30Body(body);
//			parser.parse31Body(content);
//			// parser.processBody31( (Up31Body)parser.parse31Body(body));
//		} catch (Exception e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}

    }

}
